﻿ko.bindingHandlers.runAfter = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        // add dependency on all other bindings
        ko.toJS(allBindingsAccessor());
        setTimeout(function () {
            var value = valueAccessor();
            if (typeof value != 'function' || ko.isObservable(value))
                throw new Error('run must be used with a function');
            value(element);
        });
    }
};

ko.bindingHandlers.enterPress = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        $(element).keypress(function (event) {
            var keyCode = (event.which ? event.which : event.keyCode);
            if (keyCode === 13) {
                valueAccessor().call(viewModel, viewModel); //set "this" to the data and also pass it as first arg, in case function has "this" bound
                return false;
            }
            return true;
        });
    }
};

// By: Hans Fjällemark and John Papa
// https://github.com/CodeSeven/KoLite
ko.dirtyFlag = function (objectToTrack, isInitiallyDirty, hashFunction) {

    hashFunction = hashFunction || ko.toJSON;

    var
        self = this,
        _objectToTrack = objectToTrack,
        _lastCleanState = ko.observable(hashFunction(_objectToTrack)),
        _isInitiallyDirty = ko.observable(isInitiallyDirty),

        result = function () {
            self.forceDirty = function ()
            {
                _isInitiallyDirty(true);
            };

            self.isDirty = ko.computed(function () {
                //console.debug('actu', ko.toJSON(hashFunction(_objectToTrack)));
                //console.debug('last', ko.toJSON(_lastCleanState()));
                //console.debug('');

                return _isInitiallyDirty() || hashFunction(_objectToTrack) !== _lastCleanState();
            });

            self.reset = function () {
                _lastCleanState(hashFunction(_objectToTrack));
                _isInitiallyDirty(false);
            };
            return self;
        };

    return result;

};

ko.bindingHandlers.integerValue = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var underlyingObservable = valueAccessor();
        var interceptor = ko.computed({
            read: underlyingObservable,
            write: function (newValue) {
                var current = underlyingObservable();
                newValue = $.trim(newValue || '');
                var valueToWrite = (newValue == '' || isNaN(newValue)) ? null : parseInt(newValue);

                //only write if it changed
                if (valueToWrite !== current) {
                    underlyingObservable(valueToWrite);
                } else {
                    //if the rounded value is the same as it was, but a different value was written, force a notification so the current field is updated to the rounded value
                    if (newValue !== current) {
                        underlyingObservable.valueHasMutated();
                    }
                }
            }
        });
        ko.bindingHandlers.value.init(element, function () { return interceptor; }, allBindingsAccessor);
    },
    update: ko.bindingHandlers.value.update
};

ko.bindingHandlers.floatValue = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var scale = allBindingsAccessor().scale;
        var underlyingObservable = valueAccessor();
        var interceptor = ko.computed({
            read: underlyingObservable,
            write: function (newValue) {
                var current = underlyingObservable();
                newValue = $.trim(newValue || '');
                var valueToWrite = (newValue == '' || isNaN(newValue)) ? null : parseFloat(newValue);

                if (valueToWrite != null && typeof scale != 'undefined') {
                    valueToWrite = +valueToWrite.toFixed(scale);
                }

                //only write if it changed
                if (valueToWrite !== current) {
                    underlyingObservable(valueToWrite);
                } else {
                    //if the rounded value is the same as it was, but a different value was written, force a notification so the current field is updated to the rounded value
                    if (newValue !== current) {
                        underlyingObservable.valueHasMutated();
                    }
                }
            }
        });
        ko.bindingHandlers.value.init(element, function () { return interceptor; }, allBindingsAccessor);
    },
    update: ko.bindingHandlers.value.update
};

ko.bindingHandlers.nullableChecked = {
    init: function (element, valueAccessor) {
        ko.bindingHandlers.checked.init(element, valueAccessor);
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value == null) {
            element.indeterminate = true;
        }
        else {
            element.indeterminate = false;
        };
    }
};

//http://www.robertgray.net.au/posts/2013/2/triggering-validation-from-a-custom-knockoutjs-binding
ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {  };
        $(element).datepicker(options);
        	        
        ko.utils.registerEventHandler(element, 'hide', function() {
            var widget = $(element).data('datepicker');

            var value = valueAccessor();
            if (ko.isObservable(value)) {
                if (element.value) {
                    var date = widget.getUTCDate();
                    value(date);
                    //value(moment.utc(date).toISOString())
                } else {
                    value(null);
                }

            }
        });

        ko.utils.registerEventHandler(element, 'change', function() {
            var widget = $(element).data('datepicker');

            if (widget.isVisible()) {
                return;
            }

            var value = valueAccessor();
            if (ko.isObservable(value)) {
                if (element.value) {
                    var date = widget.getUTCDate();
                    value(date);
                    //value(moment.utc(date).toISOString())
                } else {
                    value(null);
                }

            }
        });

        //bugfix: datepicker in a modal problem
        ko.utils.registerEventHandler(element, 'show.bs.modal', function (event) {
            event.stopPropagation();
        });
        ko.utils.registerEventHandler(element, 'hide.bs.modal', function (event) {
            event.stopPropagation();
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).datepicker('remove');
        });

    },
    //update the control when the view model changes
    update: function (element, valueAccessor) {
        
        var widget = $(element).data('datepicker');
        	 
        //when the view model is updated, update the widget
        if (widget) {
            widget.date = ko.utils.unwrapObservable(valueAccessor());

            if (!widget.date) {
                return;
            }

            if (typeof(widget.date) === 'string') {
                widget.setUTCDate(moment.utc(widget.date).toDate());
                return;
            }

            widget.setValue();
        }

    }
};

var windowURL = window.URL || window.webkitURL;
ko.bindingHandlers.file = {
    init: function (element, valueAccessor) {

        var bindings = ko.utils.unwrapObservable(valueAccessor() || {});

        $(element).change(function () {
            var file = this.files[0];

            //fileName
            if (bindings.fileName && ko.isObservable(bindings.fileName)) {
                if (!file) {
                    bindings.fileName(null);
                } else {
                    bindings.fileName(file.name);
                }
            }

            //fileSize
            if (bindings.fileSize && ko.isObservable(bindings.fileSize)) {
                if (!file) {
                    bindings.fileSize(0);
                } else {
                    bindings.fileSize(file.size);
                }
            }

            //fileContentType
            if (bindings.fileContentType && ko.isObservable(bindings.fileContentType)) {
                if (!file) {
                    bindings.fileContentType(null);
                } else {
                    bindings.fileContentType(file.type);
                }
            }

            //fileEncoded
            if (bindings.fileEncoded && ko.isObservable(bindings.fileEncoded)) {
                if (!file) {
                    bindings.fileEncoded(null);
                } else {
                    var reader = new FileReader();
                    reader.onload = function (e) {
                        var binary = '';
                        var bytes = new Uint8Array(e.target.result);
                        var len = bytes.byteLength;
                        for (var i = 0; i < len; i++) {
                            binary += String.fromCharCode(bytes[i]);
                        }
                        bindings.fileEncoded(window.btoa(binary));

                        if (bindings.onLoad != null) {
                            bindings.onLoad();
                        }
                    };
                    reader.readAsArrayBuffer(file);
                }
            }

            //fileObjectURL
            if (bindings.fileObjectURL && ko.isObservable(bindings.fileObjectURL)) {
                var oldUrl = bindings.fileObjectURL();
                if (oldUrl) {
                    windowURL.revokeObjectURL(oldUrl);
                }
                bindings.fileObjectURL(file && windowURL.createObjectURL(file));
            }
        });
    }
};


//Bootstrap
ko.bindingHandlers.popover = {
    init: function (element, valueAccessor) {
        var local = ko.utils.unwrapObservable(valueAccessor()),
            options = {};

        ko.utils.extend(options, local);

        $(element).popover(options);

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).popover('destroy');
        });
    }
};

ko.bindingHandlers.tooltip = {
    init: function (element, valueAccessor) {
        var local = ko.utils.unwrapObservable(valueAccessor()),
            options = {};

        ko.utils.extend(options, local);

        $(element).tooltip(options);

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).tooltip('destroy');
        });
    }
};

ko.bindingHandlers.minicolors = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var local = ko.utils.unwrapObservable(valueAccessor()),
            options = allBindingsAccessor().defaultSettings || {};

        var funcOnSelectColor = function () {
            var observable = valueAccessor();
            observable($(element).minicolors("value"));
        }
        //-- also change event to hide
        options.hide = funcOnSelectColor;

        $(element).minicolors(options);

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", funcOnSelectColor);

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).minicolors("destroy");
        });

    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        var current = $(element).minicolors("value");
        if (value - current !== 0) {
            $(element).minicolors("value", value);
        }
    }
};